#include "StdAfx.h"
#include "FreeListPool.h"
#include <iostream>

namespace WTB{

	int CFreeList::Init(int _nPerMemorySize){

		if (_nPerMemorySize <= 0)
		{
			return -1;
		}
		if (m_nPerMemorySize > 0)
		{
			return -1;
		}

		m_nPerMemorySize = _nPerMemorySize;

		m_MemoryArrayPointArray = (char**)malloc(sizeof(char*) * DEFAULT_MEMORY_ARRAY_POINT_ARRAY_LEN);
		if (NULL == m_MemoryArrayPointArray)
		{
			UnInit();
			return -1;
		}

		memset(m_MemoryArrayPointArray,0,sizeof(char*) * DEFAULT_MEMORY_ARRAY_POINT_ARRAY_LEN);

		m_nCurrentMemoryArrayPointArrayLen = DEFAULT_MEMORY_ARRAY_POINT_ARRAY_LEN;

		m_MemoryArrayPointArray[0] = 
			(char*)malloc(m_nPerMemorySize * PER_MEMORY_ARRAY_LEN);

		if (NULL == m_MemoryArrayPointArray[0])
		{
			UnInit();
			return -1;
		}
		memset(m_MemoryArrayPointArray[0],0,m_nPerMemorySize * PER_MEMORY_ARRAY_LEN);

		m_FreeListHead = m_MemoryArrayPointArray[0];
		int nMemoryIndex = 0;
		for (;nMemoryIndex < PER_MEMORY_ARRAY_LEN - 1; ++nMemoryIndex)
		{
			*((char**)&(m_FreeListHead[nMemoryIndex * m_nPerMemorySize]))
				= m_FreeListHead + (nMemoryIndex + 1) * m_nPerMemorySize;
		}
		*((char**)&(m_FreeListHead[nMemoryIndex * m_nPerMemorySize]))
			= NULL;

		m_nCurrentMemoryArrayPointArrayUsedLen = 1;

		return 0;
	}

	void CFreeList::UnInit(){
		m_FreeListHead = NULL;
		m_nPerMemorySize = 0;

		for (int nIndex = 0; nIndex < m_nCurrentMemoryArrayPointArrayUsedLen; ++nIndex)
		{
			free(m_MemoryArrayPointArray[nIndex]);
			m_MemoryArrayPointArray[nIndex] = NULL;
		}
		m_nCurrentMemoryArrayPointArrayUsedLen = 0;

		if (m_MemoryArrayPointArray)
		{
			free(m_MemoryArrayPointArray);
			m_MemoryArrayPointArray = NULL;
		}
		m_nCurrentMemoryArrayPointArrayLen = 0;

	}

	char* CFreeList::GetMemory(int _nByte){

		m_objCriticalSection.Lock();

		if (_nByte > m_nPerMemorySize)
		{
			m_objCriticalSection.Unlock();
			return NULL;
		}

		if (NULL != m_FreeListHead)
		{
			char* pReturn = m_FreeListHead;
			m_FreeListHead = *((char**)&m_FreeListHead[0]);
			*((char**)&pReturn[0]) = 0;
			m_objCriticalSection.Unlock();
			return pReturn;
		}

		if (m_nCurrentMemoryArrayPointArrayUsedLen == m_nCurrentMemoryArrayPointArrayLen)
		{
			int newMemoryArrayPointArrayLen = 2*m_nCurrentMemoryArrayPointArrayLen;
			char** newMemoryArrayPointArray = (char**)malloc(sizeof(char*) * newMemoryArrayPointArrayLen);
			if (NULL == newMemoryArrayPointArray)
			{
				m_objCriticalSection.Unlock();
				return NULL;
			}
			memset(newMemoryArrayPointArray,0,newMemoryArrayPointArrayLen);
			memcpy(newMemoryArrayPointArray,m_MemoryArrayPointArray,sizeof(char*) * m_nCurrentMemoryArrayPointArrayUsedLen);
			free(m_MemoryArrayPointArray);
			m_MemoryArrayPointArray = newMemoryArrayPointArray;
			m_nCurrentMemoryArrayPointArrayLen = newMemoryArrayPointArrayLen;

		}

		m_MemoryArrayPointArray[m_nCurrentMemoryArrayPointArrayUsedLen] = 
			(char*)malloc(m_nPerMemorySize * PER_MEMORY_ARRAY_LEN);

		if (NULL == m_MemoryArrayPointArray[m_nCurrentMemoryArrayPointArrayUsedLen])
		{
			m_objCriticalSection.Unlock();
			return NULL;
		}
		memset(m_MemoryArrayPointArray[m_nCurrentMemoryArrayPointArrayUsedLen],0,m_nPerMemorySize * PER_MEMORY_ARRAY_LEN);

		m_FreeListHead = m_MemoryArrayPointArray[m_nCurrentMemoryArrayPointArrayUsedLen];
		int nMemoryIndex = 0;
		for (;nMemoryIndex < PER_MEMORY_ARRAY_LEN - 1; ++nMemoryIndex)
		{
			*((char**)&(m_FreeListHead[ nMemoryIndex * m_nPerMemorySize]))
				= m_FreeListHead + (nMemoryIndex + 1) * m_nPerMemorySize;
		}
		*((char**)&(m_FreeListHead[nMemoryIndex * m_nPerMemorySize]))
			= NULL;

		m_nCurrentMemoryArrayPointArrayUsedLen += 1;

		char* pReturn = m_FreeListHead;
		m_FreeListHead = *((char**)&m_FreeListHead[0]);
		*((char**)&pReturn[0]) = 0;

		m_objCriticalSection.Unlock();
		return pReturn;
	}

	int	CFreeList::ReleaseMemory(char* _memory,int _nSize)
	{
		m_objCriticalSection.Lock();

		if (_nSize > m_nPerMemorySize)
		{
			m_objCriticalSection.Unlock();
			return -1;
		}
		memset(_memory,0,m_nPerMemorySize);
		*((char**)&_memory[0]) = m_FreeListHead;
		m_FreeListHead = _memory;

		m_objCriticalSection.Unlock();
		return 0;
	}


	CFreeListPool::CFreeListPool(void)
	{
		if (-1 == Init())
		{
			printf("free list pool init fail\n");
		}
	}

	CFreeListPool::~CFreeListPool(void)
	{
		UnInit();
	}

	int CFreeListPool::Init()
	{
		for (int nIndex = 0;nIndex < FREE_LIST_ARRAY_LEN; ++nIndex)
		{
			if(-1 == m_FreeListArray[nIndex].Init(MIN_MEMORY_SLICE_SIZE * (nIndex + 1)))
			{
				for (int i = 0; i < nIndex; ++i)
				{
					m_FreeListArray[i].UnInit();
				}
				return -1;
			}
		}

		return 0;
	}

	void CFreeListPool::UnInit()
	{
		for (int nIndex = 0 ; nIndex < FREE_LIST_ARRAY_LEN; ++nIndex)
		{
			m_FreeListArray[nIndex].UnInit();
		}
	}

	char* CFreeListPool::GetMemory(int _nByte)
	{
		int nIndex = (_nByte - 1) / MIN_MEMORY_SLICE_SIZE;

		if (nIndex < FREE_LIST_ARRAY_LEN)
		{
			return m_FreeListArray[nIndex].GetMemory(_nByte);
		}

		char* memory = (char*)malloc(_nByte);

		if (NULL == memory)
		{
			return NULL;
		}

		memset(memory,0,_nByte);

		return memory;
	}

	int	CFreeListPool::ReleaseMemory(char* _memory,int _nByte)
	{
		int nIndex = (_nByte - 1) / MIN_MEMORY_SLICE_SIZE;

		if (nIndex < FREE_LIST_ARRAY_LEN)
		{
			return m_FreeListArray[nIndex].ReleaseMemory(_memory,_nByte);
		}

		free(_memory);

		return 0;
	}

}